// ==UserScript==
// @name         小红书探索页面数据抓取器增强版（带定时发送API功能）
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  自动滚动小红书探索页面，抓取信息流数据（含博主昵称、发布日期），去重后支持Excel导出，定时发送全部数据到API接口
// @author       YourName
// @match        https://www.xiaohongshu.com/**
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';
    window.sendCollectedDataToApi = sendCollectedDataToApi;


    // ======================
    // 🧩 数据提取方法（原有，未改动逻辑）
    // ======================
    const CONFIG_SELECTORS = {
        feedCard: '.note-item, [class*="note"], .feed-item, [data-video-id], [class*="card"]',
        title: '.title, [class*="title"], h1, h2, h3, h4, a[href*="/explore/"]',
        image: 'img[src*="xiaohongshu"], img[src*="xhscdn"], img, .cover-image, [class*="image"], [class*="img"]',
        likes: '[class*="like"], [class*="count"], .like-count, .interact-item, button span',
        author: '[class*="author"], [class*="user"], .user-name, .author-info, [class*="nickname"], [class*="name"]',
        date: '[class*="date"], [class*="time"], .publish-time, [class*="publish"], [class*="created"]'
    };


    // ======================
    // 🔧 配置区域（可调整）
    // ======================
    const CONFIG = {
        // 滚动与抓取配置
        scrollSpeed: 50,           // 滚动速度：像素/秒
        checkInterval: 3000,       // 抓取检查间隔（毫秒）
        // maxScrollDuration: 30000000, // 最大滚动时长（毫秒，可选）

        // 定时发送配置（新增）
        sendInterval: 3000,      // 定时发送间隔（毫秒，5分钟 = 300000ms，可调整）
        apiEndpoint: 'https://api.coze.cn/api/trigger/v1/webhook/biz_id/bot_platform/hook/1000000000549160706', // ← 请替换为您的真实接口地址，能接收 POST + JSON: { input: [...] }
        enableAutoSend: true       // 是否启用自动定时发送功能
    };

    // ======================
    // 📦 状态管理
    // ======================
    let state = {
        isScrolling: true,
        isPaused: false,
        collectedData: [],
        scrollInterval: null,
        checkInterval: null,
        startTime: null,
        lastSendTime: null,
        sendStatus: '未发送'
    };

    // ======================
    // 🚀 初始化入口
    // ======================
    function init() {
        loadSavedData();
        createControlPanel();
        startAutoScroll();
        startDataCollection();

        // 启动定时发送任务（如果启用）
        if (CONFIG.enableAutoSend && CONFIG.apiEndpoint) {
            setInterval(sendCollectedDataToApi, CONFIG.sendInterval);
            console.log(`⏰ 已启动定时发送任务，每隔 ${CONFIG.sendInterval / 1000} 秒发送一次数据到接口`);
        }

        console.log('🐾 小红书数据抓取脚本已启动（增强版 + 定时发送API）');
        console.log(`📊 已有数据: ${state.collectedData.length} 条`);
        updateSendStatus(CONFIG.enableAutoSend ? '已启用' : '未启用');
    }

    // ======================
    // 📊 数据抓取与处理（原有逻辑）
    // ======================
    function startAutoScroll() {
        state.startTime = Date.now();
        state.scrollInterval = setInterval(() => {
            if (state.isScrolling && !state.isPaused) {
                window.scrollBy(0, 1);
                if ((window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight - 10) {
                    console.log('📜 已滚动到页面底部，停止滚动');
                    state.isScrolling = false;
                    updateStatus('已到底部');
                }
            }
        }, 1000 / CONFIG.scrollSpeed);
    }

    function startDataCollection() {
        collectData();
        state.checkInterval = setInterval(collectData, CONFIG.checkInterval);
    }

    function collectData() {
        if (state.isPaused) return;
        try {
            const newData = [];
            const cards = document.querySelectorAll(CONFIG.selectors.feedCard);
            console.log(`🔍 发现 ${cards.length} 个潜在卡片`);

            cards.forEach((card, index) => {
                if (index > 50) return;
                const data = extractCardData(card, index);
                if (data && data.title) {
                    if (!isDuplicate(data.title, data.author)) {
                        newData.push(data);
                    }
                }
            });

            if (newData.length > 0) {
                state.collectedData = state.collectedData.concat(newData);
                saveData();
                console.group(`🆕 新增 ${newData.length} 条数据 (总计: ${state.collectedData.length})`);
                newData.forEach(item => {
                    console.log(`📝 ${item.title} - 👤 ${item.author} - 📅 ${item.publishDate} - 👍 ${item.likes}`);
                });
                console.groupEnd();
                updateDataCount();
            }
        } catch (error) {
            console.error('数据抓取错误:', error);
        }
    }

    function extractCardData(card, index) {
        try {
            let title = extractTextContent(card, CONFIG_SELECTORS.title.split(', '));
            let author = extractTextContent(card, CONFIG_SELECTORS.author.split(', '));
            let publishDate = extractTextContent(card, CONFIG_SELECTORS.date.split(', '));
            publishDate = cleanDate(publishDate);
            let imageUrl = extractImageUrl(card);
            let likes = extractLikes(card);

            if (title) {
                return {
                    id: `card_${Date.now()}_${index}`,
                    title: title.substring(0, 200),
                    author: author || '未知博主',
                    publishDate: publishDate || '未知日期',
                    imageUrl: imageUrl || '未找到',
                    likes: likes,
                    timestamp: new Date().toISOString(),
                    source: window.location.href
                };
            }
            return null;
        } catch (error) {
            console.error(`提取卡片数据出错:`, error);
            return null;
        }
    }

    function extractTextContent(element, selectors) {
        for (let selector of selectors) {
            const found = element.querySelector(selector);
            if (found) {
                const text = found.textContent.trim();
                if (text && text.length > 0) return text;
            }
        }
        return '';
    }

    function extractImageUrl(element) {
        for (let selector of CONFIG_SELECTORS.image.split(', ')) {
            const img = element.querySelector(selector);
            if (img) {
                const url = img.src || img.getAttribute('data-src') || img.getAttribute('data-original') || '';
                if (url && url.includes('http')) return url.split('?')[0];
            }
        }
        return '';
    }

    function extractLikes(element) {
        for (let selector of CONFIG_SELECTORS.likes.split(', ')) {
            const els = element.querySelectorAll(selector);
            for (let el of els) {
                const text = el.textContent.trim();
                const match = text.match(/(\d+(\.\d+)?)[万wk]?/i);
                if (match) {
                    let num = parseFloat(match[1]);
                    if (text.includes('万') || text.includes('w')) num *= 10000;
                    if (text.includes('k')) num *= 1000;
                    return Math.round(num);
                }
            }
        }
        return 0;
    }

    function cleanDate(dateStr) {
        if (!dateStr) return '';
        if (dateStr.includes('前')) {
            const now = new Date();
            if (dateStr.includes('分钟')) {
                const m = parseInt(dateStr) || 0;
                now.setMinutes(now.getMinutes() - m);
                return now.toISOString().split('T')[0];
            } else if (dateStr.includes('小时')) {
                const h = parseInt(dateStr) || 0;
                now.setHours(now.getHours() - h);
                return now.toISOString().split('T')[0];
            } else if (dateStr.includes('天')) {
                const d = parseInt(dateStr) || 0;
                now.setDate(now.getDate() - d);
                return now.toISOString().split('T')[0];
            }
        }
        return dateStr;
    }

    function isDuplicate(title, author) {
        return state.collectedData.some(item => item.title === title && item.author === author);
    }

    // ======================
    // 📤 定时发送数据到API（新增核心功能）
    // ======================
    function sendCollectedDataToApi() {
        if (!CONFIG.enableAutoSend || !CONFIG.apiEndpoint) {
            console.log('⚠️ 自动发送未启用或接口未设置');
            updateSendStatus('自动发送未启用');
            return;
        }
        if (state.collectedData.length === 0) {
            console.log('⚠️ 暂无数据可发送');
            updateSendStatus('无数据可发送');
            return;
        }

        console.log('🚀 开始定时发送数据到接口...');
        updateSendStatus('发送中...');

        const payload = { input: state.collectedData };

        fetch(CONFIG.apiEndpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization: Bearer': 'QAHGQgSw'
            },
            body: JSON.stringify(payload)
        })
            .then(res => {
                if (!res.ok) throw new Error(`HTTP ${res.status}`);
                return res.json();
            })
            .then(data => {
                console.log('✅ 发送成功！接口返回：', data);
                updateSendStatus('发送成功');
                state.lastSendTime = new Date().toLocaleString();
            })
            .catch(err => {
                console.error('❌ 发送失败：', err);
                updateSendStatus('发送失败: ' + err.message);
            });
    }

    function updateSendStatus(msg) {
        const el = document.getElementById('xsSendStatus');
        if (el) el.textContent = `自动发送: ${msg}`;
    }

    // ======================
    // 🎛️ 用户控制面板（原有 + 新增发送状态）
    // ======================
    function createControlPanel() {
        GM_addStyle(`
            .xs-data-control-panel {
                position: fixed;
                top: 100px;
                right: 20px;
                background: rgba(255, 255, 255, 0.95);
                border: 2px solid #ff2442;
                border-radius: 10px;
                padding: 15px;
                z-index: 10000;
                font-family: system-ui, -apple-system, sans-serif;
                box-shadow: 0 4px 15px rgba(0,0,0,0.2);
                min-width: 280px;
                backdrop-filter: blur(10px);
            }
            .xs-control-title {
                font-size: 16px;
                font-weight: bold;
                color: #ff2442;
                margin-bottom: 10px;
                text-align: center;
            }
            .xs-control-btn {
                width: 100%;
                padding: 8px 12px;
                border: none;
                border-radius: 6px;
                cursor: pointer;
                font-size: 13px;
                margin-bottom: 8px;
                transition: all 0.3s;
            }
            .xs-control-btn:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
            .xs-btn-primary { background: #ff2442; color: white; }
            .xs-btn-secondary { background: #4CAF50; color: white; }
            .xs-btn-warning { background: #ff9800; color: white; }
            .xs-btn-danger { background: #f44336; color: white; }
            .xs-status-info { font-size: 11px; color: #666; text-align: center; margin-top: 5px; line-height: 1.4; }
            .xs-field-display { background: #f5f5f5; border-radius: 4px; padding: 8px; margin-top: 8px; font-size: 10px; }
            #xsSendStatus { font-size: 11px; color: #666; text-align: center; margin-top: 5px; }
        `);

        const panel = document.createElement('div');
        panel.className = 'xs-data-control-panel';
        panel.innerHTML = `
            <div class="xs-control-title">小红书数据抓取增强版</div>
            <button id="xsToggleScroll" class="xs-control-btn xs-btn-primary">暂停滚动</button>
            <button id="xsExportExcel" class="xs-control-btn xs-btn-secondary">导出Excel</button>
            <button id="xsClearData" class="xs-control-btn xs-btn-warning">清空数据</button>
            <button id="xsDebugInfo" class="xs-control-btn xs-btn-danger">调试信息</button>
            <div id="xsSendStatus">自动发送: 未启用</div>
            <div class="xs-status-info">
                <div>速度: ${CONFIG.scrollSpeed}px/秒 | 抓取: 3秒/次</div>
                <div id="xsDataCount">已收集: ${state.collectedData.length} 条</div>
                <div id="xsStatus">状态: 运行中</div>
            </div>
            <div class="xs-field-display">
                <div>抓取字段: 标题、博主、日期、点赞、图片</div>
                <div>去重方式: 标题+博主组合</div>
            </div>
        `;

        document.body.appendChild(panel);

        // 绑定控制按钮事件（原有逻辑保持不变）
        document.getElementById('xsToggleScroll').addEventListener('click', toggleScroll);
        document.getElementById('xsExportExcel').addEventListener('click', exportToExcel);
        document.getElementById('xsClearData').addEventListener('click', clearData);
        document.getElementById('xsDebugInfo').addEventListener('click', showDebugInfo);
    }

    // ======================
    // 🧰 工具函数（原有，包括导出/清除/切换/调试等）
    // ======================
    function toggleScroll() {
        state.isScrolling = !state.isScrolling;
        const btn = document.getElementById('xsToggleScroll');
        if (state.isScrolling) {
            btn.textContent = '暂停滚动';
            btn.className = 'xs-control-btn xs-btn-primary';
            updateStatus('运行中');
        } else {
            btn.textContent = '继续滚动';
            btn.className = 'xs-control-btn xs-btn-warning';
            updateStatus('已暂停');
        }
    }

    function clearData() {
        if (confirm('确定清空所有数据？不可恢复！')) {
            state.collectedData = [];
            saveData();
            updateDataCount();
            console.log('🗑️ 数据已清空');
            updateStatus('数据已清空');
        }
    }

    function updateDataCount() {
        document.getElementById('xsDataCount').textContent = `已收集: ${state.collectedData.length} 条`;
    }

    function updateStatus(msg) {
        document.getElementById('xsStatus').textContent = `状态: ${msg}`;
    }

    function saveData() {
        GM_setValue('xiaohongshu_collected_data', JSON.stringify(state.collectedData));
    }

    function loadSavedData() {
        try {
            const d = GM_getValue('xiaohongshu_collected_data', '[]');
            state.collectedData = JSON.parse(d);
        } catch (e) {
            state.collectedData = [];
            console.error('加载数据失败', e);
        }
    }

    function exportToExcel() {
        if (state.collectedData.length === 0) {
            alert('暂无数据可导出');
            return;
        }
        try {
            const excelData = state.collectedData.map(item => ({
                '标题': item.title,
                '博主昵称': item.author,
                '发布日期': item.publishDate,
                '点赞数': item.likes,
                '图片链接': item.imageUrl,
                '抓取时间': new Date(item.timestamp).toLocaleString('zh-CN'),
                '数据ID': item.id,
                '来源页面': item.source
            }));
            const sheet = XLSX.utils.json_to_sheet(excelData);
            const book = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(book, sheet, '小红书数据');
            const cols = [
                {wch: 40}, {wch: 15}, {wch: 12}, {wch: 10}, {wch: 30}, {wch: 20}, {wch: 20}, {wch: 25}
            ];
            sheet['!cols'] = cols;
            XLSX.writeFile(book, `小红书完整数据_${Date.now()}.xlsx`);
            console.log('✅ Excel已导出');
            updateStatus('导出成功');
        } catch (e) {
            alert('导出失败，请查看控制台');
            console.error(e);
        }
    }

    function showDebugInfo() {
        console.group('🐛 调试信息');
        console.log('总数据量:', state.collectedData.length);
        console.log('当前选择器配置:', CONFIG_SELECTORS);
        const cards = document.querySelectorAll(CONFIG_SELECTORS.feedCard);
        console.log('页面卡片数:', cards.length);
        console.groupEnd();
        updateStatus('调试信息已输出');
    }

    // 鼠标悬停暂停（原有）
    document.addEventListener('mouseover', (e) => {
        if (!e.target.closest('.xs-data-control-panel')) {
            state.isPaused = true;
            updateStatus('鼠标悬停暂停');
        }
    });
    document.addEventListener('mouseout', (e) => {
        if (!e.target.closest('.xs-data-control-panel')) {
            state.isPaused = false;
            updateStatus('运行中');
        }
    });

    // ======================
    // 🏁 启动
    // ======================
    window.addEventListener('load', init);
})();